#include "dhnetsdk.h"

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <iostream>
using namespace std;
FILE       *m_pFile;
LONG        lPort;
BOOL        g_bNetSDKInitFlag = FALSE;
static char g_szDevIp[32]     = "xxx.xxx.xxx.xxx";
static WORD g_nPort = 8000; // tcp 连接端口，需与期望登录设备页面 tcp 端口配置一致
static char g_szUserName[64] = "admin";
static char g_szPasswd[64]   = "admin";
//*********************************************************************************
// 常用回调集合声明
// 设备断线回调函数
// 不建议在该回调函数中调用 SDK 接口
// 通过 CLIENT_Init 设置该回调函数，当设备出现断线时，SDK 会调用该函数
void CALLBACK DisConnectFunc(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, LDWORD dwUser) {}
// 断线重连成功回调函数
// 不建议在该回调函数中调用 SDK 接口
// 通过 CLIENT_SetAutoReconnect 设置该回调函数，当已断线的设备重连成功时，SDK 会调用该函数
void CALLBACK HaveReConnect(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, LDWORD dwUser) {}
// 实时监视数据回调函数
// 不建议在该回调函数中调用 SDK 接口

// 通过 CLIENT_SetRealDataCallBackEx 设置该回调函数，当收到实时监视数据时，SDK 会调用该函数
// 建议用户在此回调函数中只进行保存数据的操作，不建议用户在回调函数里直接对数据进行编解码等处理
// 即：将相应的数据拷贝到自己的存储空间，离开回调函数后再对数据做编解码等处理
void CALLBACK RealDataCallBackEx(LLONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize,
                                 LONG param, LDWORD dwUser)
{
}
//*********************************************************************************

int save_h264_file(BYTE *frame, DWORD dwBufSize)
{
    if (NULL != frame) {
        static int find_I = 0;
        if (!find_I) {
            if (frame[0] == 0x00 && frame[1] == 0x00 && frame[2] == 0x00 && frame[3] == 0x01) {
                if ((frame[4] & 0x1f) == 7) {
                    find_I = 1;
                    fwrite(frame, 1, dwBufSize, m_pFile);
                    fflush(m_pFile);
                }
            }
        } else {
            fwrite(frame, 1, dwBufSize, m_pFile);
            fflush(m_pFile);
        }
    }

    return 0;
}

void CALLBACK esDataCallBackex(LLONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize,
                               LONG param, LDWORD dwUser)
{
    printf("-----------------%d, %d\n", dwBufSize, dwDataType);
    // save_h264_file(pBuffer, dwBufSize);
    if ((pBuffer[4] & 0x1f) == 7) {
        printf("-----------------------------------I帧\n");
    }
    fwrite(pBuffer, 1, dwBufSize, m_pFile);
    fflush(m_pFile);
}
void CALLBACK esDataCallBack(LLONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize,
                             LDWORD dwUser)
{
    printf("-----------------%d, %d\n", dwBufSize, dwDataType);
    save_h264_file(pBuffer, dwBufSize);
}

void CALLBACK cbRealData(LLONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, LONG param,
                         LDWORD dwUser)
{
    printf("-----------------%d, %d\n", dwBufSize, dwDataType);
    // 这里dwDataType 为0表示 原始数据标志 1 表示 带有帧信息的数据标志 2 表示yuv数据  3 表示 pcm音频数据
    fwrite(pBuffer, 1, dwBufSize, m_pFile);
    fflush(m_pFile);
}

void CALLBACK disConnect(LLONG lOperateHandle, EM_REALPLAY_DISCONNECT_EVENT_TYPE dwEventType, void *param,
                         LDWORD dwUser)
{
}

int main()
{
    std::string file = "./test.h264";
    if (nullptr == m_pFile) {
        if (!access(file.c_str(), F_OK)) {
            remove(file.c_str());
        }
        m_pFile = fopen(file.c_str(), "wb");
    }

    // SDK 初始化
    g_bNetSDKInitFlag = CLIENT_Init(DisConnectFunc, 0);
    if (FALSE == g_bNetSDKInitFlag) {
        printf("Initialize client SDK fail; \n");
        return 0;
    } else {
        printf("Initialize client SDK done; \n");
    }

    // 设置连接时间与重连时间
    CLIENT_SetAutoReconnect(&HaveReConnect, 0);
    CLIENT_SetConnectTime(5000, 3);
    NET_PARAM stuNetParm    = {0};
    stuNetParm.nConnectTime = 3000; // 登录时尝试建立链接的超时时间
    CLIENT_SetNetworkParam(&stuNetParm);

    // 登录设备
    NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY  stInparam;
    NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY stOutparam;
    memset(&stInparam, 0, sizeof(stInparam));

    stInparam.emSpecCap = EM_LOGIN_SPEC_CAP_TCP;
    stInparam.dwSize    = sizeof(stInparam);

    // 注册设备
    LLONG g_lLoginHandle = 0L;
    strncpy(stInparam.szIP, g_szDevIp, sizeof(stInparam.szIP) - 1);
    strncpy(stInparam.szPassword, g_szPasswd, sizeof(stInparam.szPassword) - 1);
    strncpy(stInparam.szUserName, g_szUserName, sizeof(stInparam.szUserName) - 1);
    stInparam.nPort     = g_nPort; // 设备服务端口
    stInparam.emSpecCap = EM_LOGIN_SPEC_CAP_TCP;
    g_lLoginHandle      = CLIENT_LoginWithHighLevelSecurity(&stInparam, &stOutparam);
    if (g_lLoginHandle == 0) {
        printf("CLIENT_LoginWithHighLevelSecurity %s:%d Failed! Last Error code: %x\n", stInparam.szIP,
               stInparam.nPort, CLIENT_GetLastError());
        return 0;
    } else {
        printf("CLIENT_LoginWithHighLevelSecurity %s:%d Success\n", stInparam.szIP, stInparam.nPort);
    }
#if 1
    // 启动预览并设置回调数据流
    int   nChannelID      = 0;
    LLONG lRealPlayHandle = 0;
    DH_RealPlayType emRealPlayType  = DH_RType_Realplay; // 实时监视
    lRealPlayHandle = CLIENT_RealPlayEx(g_lLoginHandle, 0, NULL, emRealPlayType);
    if (lRealPlayHandle == 0) {
        printf("CLIENT_RealPlayEx error, error code: %x\n", CLIENT_GetLastError());
        CLIENT_Cleanup();
        CLIENT_Logout(g_lLoginHandle);
        return 0;
    } else {
        printf("CLIENT_RealPlayEx success!\n");
    }

    DWORD dwFlag = 0x00000001;
    CLIENT_SetRealDataCallBackEx(lRealPlayHandle, esDataCallBackex, NULL, dwFlag);
    // CLIENT_SetRealDataCallBackEx(lRealPlayHandle, esDataCallBackex, NULL, dwFlag);
    // CLIENT_SetRealDataCallBack(lRealPlayHandle, esDataCallBack, NULL);
#else
    LLONG           lRealPlayHandle = 0;
    DH_RealPlayType emRealPlayType  = DH_RType_Realplay_0; // 实时监视
    lRealPlayHandle =
        CLIENT_StartRealPlay(g_lLoginHandle, 0, NULL, DH_RType_Realplay, cbRealData, disConnect, NULL);
#endif

    pause();
    // 注销用户
    CLIENT_Logout(g_lLoginHandle);
    CLIENT_Cleanup();
    return 0;
}
